package cn.lijiguo.concurrency.threadpool;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <pre>ThreadPoolExecutor</pre>
 * <p>
 *     Executors 工具类提供了一些方便的方法，适用于许多常见的情况
 *     而ThreadPoolExecutor则提供了高度可定制化的选项，适用于需要更精细控制的场景
 *     <b>Alibaba Java 开发手册推荐使用ThreadPoolExecutor来创建线程池</b>
 * </p>
 * <p>
 *     ThreadPoolExecutor是Java Executor框架的底层实现，它提供了一个可灵活配置的线程池。
 *     相比于Executors工具类提供的工厂方法，
 *     <b>ThreadPoolExecutor 允许你更详细地配置线程池的行为，包括核心线程数、最大线程数、线程存活时间、任务队列等。</b>
 * </p>
 * <p>
 *     public ThreadPoolExecutor(
 *          int corePoolSize,  // 核心线程数
 *          int maximumPoolSize, // 最大线程数
 *          long keepAliveTime,  // 线程空闲时间
 *          TimeUnit unit,       // 时间单位：指定 `keepAliveTime` 的时间单位，例如 TimeUnit.SECONDS。
 *          BlockingQueue<Runnable> workQueue, // 任务队列
 *          ThreadFactory threadFactory,  // 线程工厂
 *          RejectedExecutionHandler handler  // 拒绝策略
 *      )
 * </p>
 * <li>核心线程数（corePoolSize）： 线程池中一直存活的线程数量，即使它们处于空闲状态。
 *      核心线程会一直存在，不会因为任务的执行完毕而被回收，除非设置了允许核心线程超时回收。</li>
 * <li>最大线程数（maximumPoolSize）： 线程池中允许的最大线程数量。
 *      当队列满了并且当前线程数小于最大线程数时，会创建新的线程来处理任务。</li>
 * <li>线程空闲时间（keepAliveTime）和时间单位（unit）： 非核心线程的空闲时间超过这个时间，就会被回收。
 *      指定数值和时间单位一起使用。</li>
 * <li>任务队列（workQueue）： 用于保存等待执行的任务的阻塞队列。不同类型的队列有不同的特性，
 *      如 LinkedBlockingQueue 是无界队列，而 ArrayBlockingQueue 是有界队列。</li>
 * <li>线程工厂（threadFactory）： 用于创建新线程的工厂，允许对线程的创建进行定制。
 *      可以设置线程的名称、优先级、是否为守护线程等。</li>
 * <li>拒绝策略（handler）： 当任务无法被提交执行时，会使用该策略来处理。
 *      例如，可以选择默认的抛出异常、丢弃任务、调用调用者的线程来执行等。</li>
 * @author lijiguo
 */
@Slf4j
public class ThreadPoolExecutorDemo {

    public static void main(String[] args) {
        // 创建ThreadPoolExecutor
        // int corePoolSize, 核心线程数
        // int maximumPoolSize, 最大线程数
        // long keepAliveTime, 线程空闲时间
        // TimeUnit unit, 线程空闲时间单位
        // BlockingQueue<Runnable> workQueue, 任务队列
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 1,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));

        // 提交任务
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                log.info("Task " + taskId + " is executing by " + Thread.currentThread().getName());
                try {
                    // 模拟任务执行
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        // 用于优雅地关闭线程池，调用带方法后，线程池不再接受新地提交任务
        // 但是会继续执行已经提交的任务，指导所有任务执行完成
        // 之后，将线程池关闭，释放所有资源
        executor.shutdown();
    }
}
